﻿using System.Collections.Generic;
using System.Security.Principal;

namespace Sidvall.AuthorizationManager.Data
{
	public class ResourceItem
	{
		#region Public Members

		public ResourceAccessLevel AccessLevel { get; set; }
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
		public ClaimItemCollectionDictionary ClaimItemIndex { get; set; }
		public int Priority { get; set; }
		public bool ValidForResourceActionItems { get; set; }

		#region Action

		private string _Action = string.Empty;
		public string Action
		{
			get
			{
				return _Action;
			}
			set
			{
				value = Sidvall.Business.ValidationManager.GetValidString(value, false, null);
				_Action = value;
			}
		}

		#endregion
		#region SubAction

		private string _SubAction = string.Empty;
		public string SubAction
		{
			get
			{
				return _SubAction;
			}
			set
			{
				value = Sidvall.Business.ValidationManager.GetValidString(value, false, null);
				_SubAction = value;
			}
		}

		#endregion

		#region GetClaimItem

		public ClaimItem GetClaimItem(string claimType, string value)
		{
			return this.ClaimItemIndex.GetClaimItem(claimType, value);
		}

		#endregion
		#region GetDescription

		public string GetDescription()
		{
			return GetDescription(false, false, " - ");
		}
		public string GetDescription(bool includeAction, bool includeSubAction, string claimSeparator)
		{
			string item = string.Empty;
			if ((includeAction) && (!string.IsNullOrWhiteSpace(this.Action)))
				item += this.Action;
			if ((includeSubAction) && (!string.IsNullOrWhiteSpace(this.SubAction)))
			{
				if (!string.IsNullOrWhiteSpace(item))
					item += AuthorizationScopeItemManager.ResourceItemSeparator;
				item += this.Action;
			}
			if (!string.IsNullOrWhiteSpace(item))
				item += ": ";
			switch (this.AccessLevel)
			{
				case ResourceAccessLevel.AuthenticatedAndClaims:
				case ResourceAccessLevel.Claims:
					var claimDescription = this.ClaimItemIndex.GetDescription();
					if (!string.IsNullOrWhiteSpace(claimDescription))
					{
						return item + this.AccessLevel.ToString() + claimSeparator + claimDescription;
					}
					else
					{
						return item + ResourceAccessLevel.None.ToString();
					}
				default:
					return item + this.AccessLevel.ToString();
			}
		}

		#endregion
		#region IsAuthorized

		public bool IsAuthorized(IPrincipal principal, bool userIsValid, Sidvall.Security.ResourceItem resourceItem)
		{
			Sidvall.Security.ClaimCollection principalClaims;
			ClaimItem claimItem;

			switch (this.AccessLevel)
			{
				case ResourceAccessLevel.Public:
					SetAccessLevel(resourceItem, Sidvall.Security.ResourceItemAccessLevel.Public);
					return true;
				case ResourceAccessLevel.Authenticated:
					if ((principal == null) || (principal.Identity == null) || (!principal.Identity.IsAuthenticated) || (!userIsValid))
					{
						SetAccessLevel(resourceItem, Sidvall.Security.ResourceItemAccessLevel.None);
						return false;
					}
					SetAccessLevel(resourceItem, Sidvall.Security.ResourceItemAccessLevel.Protected);
					return true;
				case ResourceAccessLevel.AuthenticatedAndClaims:
					if ((principal == null) || (principal.Identity == null) || (!principal.Identity.IsAuthenticated) || (!userIsValid))
					{
						SetAccessLevel(resourceItem, Sidvall.Security.ResourceItemAccessLevel.None);
						return false;
					}
					principalClaims = Sidvall.SystemContext.Current.ClaimManager.FindClaims(principal.Identity);
					if (principalClaims != null)
					{
						foreach (var claim in principalClaims)
						{
							claimItem = GetClaimItem(claim.ClaimType, claim.Value);
							if (claimItem != null)
							{
								SetAccessLevel(resourceItem, Sidvall.Security.ResourceItemAccessLevel.Protected);
								return true;
							}
						}
					}
					SetAccessLevel(resourceItem, Sidvall.Security.ResourceItemAccessLevel.None);
					return false;
				case ResourceAccessLevel.Claims:
					if (principal == null)
					{
						SetAccessLevel(resourceItem, Sidvall.Security.ResourceItemAccessLevel.None);
						return false;
					}
					principalClaims = Sidvall.SystemContext.Current.ClaimManager.FindClaims(principal.Identity);
					if (principalClaims != null)
					{
						foreach (var claim in principalClaims)
						{
							claimItem = GetClaimItem(claim.ClaimType, claim.Value);
							if (claimItem != null)
							{
								SetAccessLevel(resourceItem, Sidvall.Security.ResourceItemAccessLevel.Protected);
								return true;
							}
						}
					}
					SetAccessLevel(resourceItem, Sidvall.Security.ResourceItemAccessLevel.None);
					return false;
				default:
					SetAccessLevel(resourceItem, Sidvall.Security.ResourceItemAccessLevel.None);
					return false;
			}
		}

		#endregion
		#region IsMatch

		public bool IsMatch(string action)
		{
			return this.Action.Equals(action, System.StringComparison.OrdinalIgnoreCase);
		}
		public bool IsMatch(string action, string subAction)
		{
			return this.Action.Equals(action, System.StringComparison.OrdinalIgnoreCase) && this.SubAction.Equals(subAction, System.StringComparison.OrdinalIgnoreCase);
		}

		#endregion

		#endregion
		#region Public Members

		#region SetAccessLevel

		private void SetAccessLevel(Sidvall.Security.ResourceItem resourceItem, Sidvall.Security.ResourceItemAccessLevel accessLevel)
		{
			switch (accessLevel)
			{
				case Sidvall.Security.ResourceItemAccessLevel.Protected:
					resourceItem.Description = GetDescription();
					resourceItem.AccessLevel = Sidvall.Security.ResourceItemAccessLevel.Protected;
					break;
				default:
					resourceItem.Description = null;
					resourceItem.AccessLevel = Sidvall.Security.ResourceItemAccessLevel.Protected;
					break;
			}
		}

		#endregion

		#endregion
		#region Constructors

		public ResourceItem()
		{
		}
		public ResourceItem(ResourceAccessLevel accessLevel, IEnumerable<IClaimItem> claims, bool validForResourceActionItems)
			: this(accessLevel, claims, validForResourceActionItems, null, null)
		{
		}
		public ResourceItem(ResourceAccessLevel accessLevel, IEnumerable<IClaimItem> claims, bool validForResourceActionItems, string action, string subAction)
		{
			this.AccessLevel = accessLevel;
			this.Action = action;
			this.SubAction = subAction;
			if (string.IsNullOrWhiteSpace(this.Action))
				this.Priority = 3;
			else if (string.IsNullOrWhiteSpace(this.SubAction))
				this.Priority = 2;
			else
				this.Priority = 1;
			this.ValidForResourceActionItems = validForResourceActionItems;
			this.ClaimItemIndex = new ClaimItemCollectionDictionary(System.StringComparer.OrdinalIgnoreCase);
			this.ClaimItemIndex.Load(claims);
		}

		#endregion
	}
}
